/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER)

#include "trace_log.h"
#include "umc_avs_dec_bit_stream.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_slice_header.h"

namespace UMC
{

Ipp8u AVSDecMoreZerosTable[128] =
{
    7,
    6,
    5, 5,
    4, 4, 4, 4,
    3, 3, 3, 3, 3, 3, 3, 3,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

bool DecodePicHeader(AVS_PICTURE_HEADER *pPicHeader,
                     AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader)
{
    // reset header
    memset(pPicHeader, 0, sizeof(AVS_PICTURE_HEADER));

    WRITE_TRACE_LOG(_dec_hdr, "picture header", 0);

    // read the start code
    SkipBits(pCtx, 24);

    // get picture type
    pPicHeader->picture_start_code = GetBits(pCtx, 8);
    WRITE_TRACE_LOG(_dec_hdr, "picture_start_code", pPicHeader->picture_start_code);

    // read information about I frame
    if (I_PICTURE_START_CODE == pPicHeader->picture_start_code)
    {
        pPicHeader->PictureType = AVS_I_PICTURE;
        pPicHeader->bbv_delay = GetBits(pCtx, 16);
        WRITE_TRACE_LOG(_dec_hdr, "bbv_delay", pPicHeader->bbv_delay);
        pPicHeader->time_code_flag = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "time_code_flag", pPicHeader->time_code_flag);
        if (pPicHeader->time_code_flag)
        {
            pPicHeader->time_code = GetBits(pCtx, 24);
            WRITE_TRACE_LOG(_dec_hdr, "time_code", pPicHeader->time_code);
        }
        // NOTE: it is absent in standard, but
        // reference code has the following 1 bit skipping
        SkipBits(pCtx, 1);
        pPicHeader->picture_distance = GetBits(pCtx, 8);
        WRITE_TRACE_LOG(_dec_hdr, "picture_distance", pPicHeader->picture_distance);
        if (pSeqHeader->low_delay)
        {
            pPicHeader->bbv_check_times = GetUE(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "bbv_check_times", pPicHeader->bbv_check_times);
        }
        pPicHeader->progressive_frame = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "progressive_frame", pPicHeader->progressive_frame);
        if (0 == pPicHeader->progressive_frame)
        {
            pPicHeader->picture_structure = GetBit(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "picture_structure", pPicHeader->picture_structure);
        }
        else
            pPicHeader->picture_structure = 1;
        pPicHeader->top_field_first = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "top_field_first", pPicHeader->top_field_first);
        pPicHeader->repeat_first_field = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "repeat_first_field", pPicHeader->repeat_first_field);
        pPicHeader->fixed_picture_qp = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "fixed_picture_qp", pPicHeader->fixed_picture_qp);
        pPicHeader->picture_qp = GetBits(pCtx, 6);
        WRITE_TRACE_LOG(_dec_hdr, "picture_qp", pPicHeader->picture_qp);
        if ((0 == pPicHeader->progressive_frame) &&
            (0 == pPicHeader->picture_structure))
        {
            pPicHeader->skip_mode_flag = GetBit(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "skip_mode_flag", pPicHeader->skip_mode_flag);
        }
        SkipBits(pCtx, 4);
        pPicHeader->loop_filter_disable = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "loop_filter_disable", pPicHeader->loop_filter_disable);
        if (0 == pPicHeader->loop_filter_disable)
        {
            pPicHeader->loop_filter_parameter_flag = GetBit(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "loop_filter_parameter_flag", pPicHeader->loop_filter_parameter_flag);
            if (pPicHeader->loop_filter_parameter_flag)
            {
                pPicHeader->alpha_c_offset = GetSE(pCtx);
                WRITE_TRACE_LOG(_dec_hdr, "alpha_c_offset", pPicHeader->alpha_c_offset);
                pPicHeader->beta_offset = GetSE(pCtx);
                WRITE_TRACE_LOG(_dec_hdr, "beta_offset", pPicHeader->beta_offset);
            }
        }
    }
    // read information about P,B frame
    else
    {
        pPicHeader->bbv_delay = GetBits(pCtx, 16);
        WRITE_TRACE_LOG(_dec_hdr, "bbv_delay", pPicHeader->bbv_delay);
        pPicHeader->picture_coding_type = GetBits(pCtx, 2);
        WRITE_TRACE_LOG(_dec_hdr, "picture_coding_type", pPicHeader->picture_coding_type);
        pPicHeader->PictureType = (1 == pPicHeader->picture_coding_type) ? (AVS_P_PICTURE) : (AVS_B_PICTURE);
        //WRITE_TRACE_LOG(_dec_hdr, "PictureType", pPicHeader->PictureType);
        pPicHeader->picture_distance = GetBits(pCtx, 8);
        WRITE_TRACE_LOG(_dec_hdr, "picture_distance", pPicHeader->picture_distance);
        if (pSeqHeader->low_delay)
        {
            pPicHeader->bbv_check_times = GetUE(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "bbv_check_times", pPicHeader->bbv_check_times);
        }

        pPicHeader->progressive_frame = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "progressive_frame", pPicHeader->progressive_frame);

        if (0 == pPicHeader->progressive_frame)
        {
            pPicHeader->picture_structure = GetBit(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "picture_structure", pPicHeader->picture_structure);
            if (0 == pPicHeader->picture_structure)
            {
                pPicHeader->advanced_pred_mode_disable = GetBit(pCtx);
                WRITE_TRACE_LOG(_dec_hdr, "advanced_pred_mode_disable", pPicHeader->advanced_pred_mode_disable);
            }
        }
        else
            pPicHeader->picture_structure = 1;
        pPicHeader->top_field_first = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "top_field_first", pPicHeader->top_field_first);
        pPicHeader->repeat_first_field = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "repeat_first_field", pPicHeader->repeat_first_field);
        pPicHeader->fixed_picture_qp = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "fixed_picture_qp", pPicHeader->fixed_picture_qp);
        pPicHeader->picture_qp = GetBits(pCtx, 6);
        WRITE_TRACE_LOG(_dec_hdr, "picture_qp", pPicHeader->picture_qp);
        WRITE_TRACE_LOG(_dec_hdr, "picture_structure", pPicHeader->picture_structure);

        if ((AVS_P_PICTURE == pPicHeader->PictureType) ||
            (0 == pPicHeader->picture_structure))
        {
            pPicHeader->picture_reference_flag = GetBit(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "picture_reference_flag", pPicHeader->picture_reference_flag);
        }
        pPicHeader->no_forward_reference_flag = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "no_forward_reference_flag", pPicHeader->no_forward_reference_flag);
        SkipBits(pCtx, 3);
        pPicHeader->skip_mode_flag = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "skip_mode_flag", pPicHeader->skip_mode_flag);
        pPicHeader->loop_filter_disable = GetBit(pCtx);
        WRITE_TRACE_LOG(_dec_hdr, "loop_filter_disable", pPicHeader->loop_filter_disable);
        if (0 == pPicHeader->loop_filter_disable)
        {
            pPicHeader->loop_filter_parameter_flag = GetBit(pCtx);
            WRITE_TRACE_LOG(_dec_hdr, "loop_filter_parameter_flag", pPicHeader->loop_filter_parameter_flag);
            if (pPicHeader->loop_filter_parameter_flag)
            {
                pPicHeader->alpha_c_offset = GetSE(pCtx);
                WRITE_TRACE_LOG(_dec_hdr, "alpha_c_offset", pPicHeader->alpha_c_offset);
                pPicHeader->beta_offset = GetSE(pCtx);
                WRITE_TRACE_LOG(_dec_hdr, "beta_offset", pPicHeader->beta_offset);
            }
        }
    }

    return true;

} // bool DecodePicHeader(AVS_PICTURE_HEADER *pPicHeader,

bool DecodeSlcHeader(AVS_SLICE_HEADER *pSlcHeader,
                     AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader,
                     const AVS_PICTURE_HEADER *pPicHeader)
{
    Ipp32s MbRow, MbIndex, MbWidth, MbHeight;

    // reset header
    memset(pSlcHeader, 0, sizeof(AVS_SLICE_HEADER));

    // read the start code
    SkipBits(pCtx, 24);

    // read information
    pSlcHeader->slice_vertical_position = GetBits(pCtx, 8);
    WRITE_TRACE_LOG(_slc_dec, "slice_vertical_position", pSlcHeader->slice_vertical_position);
    if (2800 < pSeqHeader->vertical_size)
    {
        pSlcHeader->slice_vertical_position_extension = GetBits(pCtx, 3);
        WRITE_TRACE_LOG(_slc_dec, "slice_vertical_position_extension", pSlcHeader->slice_vertical_position_extension);
    }
    if (0 == pPicHeader->fixed_picture_qp)
    {
        pSlcHeader->fixed_slice_qp = GetBit(pCtx);
        WRITE_TRACE_LOG(_slc_dec, "fixed_slice_qp", pSlcHeader->fixed_slice_qp);
        pSlcHeader->slice_qp = GetBits(pCtx, 6);
        WRITE_TRACE_LOG(_slc_dec, "slice_qp", pSlcHeader->slice_qp);
    }

    // calculate frame dimensions
    MbWidth = (pSeqHeader->horizontal_size + 15) / 16;
    if (pPicHeader->picture_structure)
        MbHeight = ((pSeqHeader->vertical_size + 15) / 16);
    else
        MbHeight = 2 * ((pSeqHeader->vertical_size + 31) / 32);

    // calculate the slice position
    MbRow = pSlcHeader->slice_vertical_position;
    if (MbRow > MbHeight)
        return false;
    if (2800 < pSeqHeader->vertical_size)
        MbRow += (pSlcHeader->slice_vertical_position_extension << 7);
    MbIndex = MbRow * MbWidth;
    pSlcHeader->first_mb = MbIndex;

    // correct first mb number when the second field is decoded
    if ((0 == pPicHeader->picture_structure) &&
        (MbWidth * MbHeight / 2 <= MbIndex))
    {
        pSlcHeader->first_mb -= MbWidth * MbHeight / 2;
        pSlcHeader->isSecondField = 1;
    }

    if ((AVS_I_PICTURE != pPicHeader->PictureType) ||
        (pSlcHeader->isSecondField))
    {
        pSlcHeader->slice_weighting_flag = GetBit(pCtx);
        WRITE_TRACE_LOG(_slc_dec, "slice_weighting_flag", pSlcHeader->slice_weighting_flag);
        if (pSlcHeader->slice_weighting_flag)
        {
            Ipp32s idx, NumberOfReferenceFwd, NumberOfReferenceBwd;

            // calculate number of references
            NumberOfReferenceFwd = 0;
            NumberOfReferenceBwd = 0;
            // second field of I frame is always P field
            if (AVS_I_PICTURE == pPicHeader->PictureType)
            {
                NumberOfReferenceFwd = 1;
            }
            else if (AVS_P_PICTURE == pPicHeader->PictureType)
            {
                if (pPicHeader->picture_structure)
                    NumberOfReferenceFwd = 2;
                else
                    NumberOfReferenceFwd = 4;
            }
            else
            {
                if (pPicHeader->picture_structure)
                {
                    NumberOfReferenceFwd = 1;
                    NumberOfReferenceBwd = 1;
                }
                else
                {
                    NumberOfReferenceFwd = 2;
                    NumberOfReferenceBwd = 2;
                }
            }

            // forward and backward weighted prediction parameters
            // go in alternate order. That is why following code is
            // sligntly ugly.
            idx = 0;
            while (NumberOfReferenceFwd + NumberOfReferenceBwd)
            {
                if (NumberOfReferenceFwd)
                {
                    NumberOfReferenceFwd -= 1;

                    // read forward weighting parameters
                    pSlcHeader->luma_scale[AVS_FORWARD][idx] = GetBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "luma_scale", pSlcHeader->luma_scale[AVS_FORWARD][idx]);
                    pSlcHeader->luma_shift[AVS_FORWARD][idx] = GetSignedBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "luma_shift", pSlcHeader->luma_shift[AVS_FORWARD][idx]);
                    SkipBits(pCtx, 1);
                    pSlcHeader->chroma_scale[AVS_FORWARD][idx] = GetBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "chroma_scale", pSlcHeader->chroma_scale[AVS_FORWARD][idx]);
                    pSlcHeader->chroma_shift[AVS_FORWARD][idx] = GetSignedBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "chroma_shift", pSlcHeader->chroma_shift[AVS_FORWARD][idx]);
                    SkipBits(pCtx, 1);
                }

                if (NumberOfReferenceBwd)
                {
                    NumberOfReferenceBwd -= 1;

                    // read backward weighting parameters
                    pSlcHeader->luma_scale[AVS_BACKWARD][idx] = GetBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "luma_scale", pSlcHeader->luma_scale[AVS_BACKWARD][idx]);
                    pSlcHeader->luma_shift[AVS_BACKWARD][idx] = GetSignedBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "luma_shift", pSlcHeader->luma_shift[AVS_BACKWARD][idx]);
                    SkipBits(pCtx, 1);
                    pSlcHeader->chroma_scale[AVS_BACKWARD][idx] = GetBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "chroma_scale", pSlcHeader->chroma_scale[AVS_BACKWARD][idx]);
                    pSlcHeader->chroma_shift[AVS_BACKWARD][idx] = GetSignedBits(pCtx, 8);
                    WRITE_TRACE_LOG(_slc_dec, "chroma_shift", pSlcHeader->chroma_shift[AVS_BACKWARD][idx]);
                    SkipBits(pCtx, 1);
                }

                // increment writing index
                idx += 1;
            }
            pSlcHeader->mb_weighting_flag = GetBit(pCtx);
            WRITE_TRACE_LOG(_slc_dec, "mb_weighting_flag", pSlcHeader->mb_weighting_flag);
        }
    }

    return true;

} // bool DecodeSlcHeader(AVS_SLICE_HEADER *pSlcHeader,

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER)
